import { Callout } from '@theguild/components'

# Loading GraphQL Schemas from Different Sources

These utils are useful for scanning, loading and building a GraphQL schema from any input.

You can specify a GraphQL endpoint, local introspection JSON file, code file that `export`s a
GraphQLSchema, AST string and `.graphql` files (with support for `glob` expression).

All found schema files can be merged into a complete schema. There is support for `#import` syntax
(formerly known as [`graphql-import`](https://github.com/ardatan/graphql-import)).

The user is given the option of implementing their own loader (implement the interface
`SchemaLoader`).

The schema loading until is using loaders, and implemented using
[chain-of-responsibility pattern](https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern).

Specifying the loader is not necessary. The user needs only provide the inputs. The utils will
detect it automatically.

For notes on typescript, refer to [loaders](/docs/schema-loading#loaders)

<Callout type="warning">
  Schema and document loading doesn't work non Node.js environments, and if you are using a bundler
  like webpack, rollup or Vite, you cannot use dynamic loaders.
</Callout>

## Load `typeDefs`/`DocumentNode` and `resolvers` from Files

```ts
const { loadFiles } = require('@graphql-tools/load-files')
const { createYoga, createSchema } = require('graphql-yoga')
const { createServer } = require('http')

async function main() {
  const schema = createSchema({
    typeDefs: await loadFiles('src/typeDefs/**/*.graphql'),
    resolvers: await loadFiles('src/resolvers/**/*.{js,ts}')
  })

  const yoga = createYoga({
    schema
  })

  const server = createServer(yoga)

  server.listen(4000, () => {
    console.log('Server is running on http://localhost:4000')
  })
}

main().catch(error => {
  console.error(error)
  process.exit(1)
})
```

<Callout type="warning">
  `loadFiles` doesn't support `#import` syntax. See below if you need that.
</Callout>

## Load `GraphQLSchema` by Using Different Loaders from Different Sources

```ts
const { loadSchema } = require('@graphql-tools/load')
const { UrlLoader } = require('@graphql-tools/url-loader')
const { JsonFileLoader } = require('@graphql-tools/json-file-loader')
const { GraphQLFileLoader } = require('@graphql-tools/graphql-file-loader')

async function main() {
  // Load from string w/ no loaders
  const schema1 = await loadSchema('type A { foo: String }')

  // Load from endpoint
  const schema2 = await loadSchema('http://localhost:3000/graphql', { loaders: [new UrlLoader()] })

  // Load from local json file
  const schema3 = await loadSchema('./schema.json', { loaders: [new JsonFileLoader()] })

  // Load from a single schema file
  const schema4 = await loadSchema('schema.graphql', { loaders: [new GraphQLFileLoader()] })

  // Load from multiple files using glob
  const schema5 = await loadSchema('./src/**/*.graphql', { loaders: [new GraphQLFileLoader()] })
}

main()
```

### Using `#import` Expression

Assume the following directory structure:

```text
.
├── schema.graphql
├── posts.graphql
└── comments.graphql
```

```graphql filename="schema.graphql"
#import Post from "posts.graphql"

type Query {
  posts: [Post]
}
```

```graphql filename="posts.graphql"
#import Comment from 'comments.graphql'

type Post {
  comments: [Comment]
  id: ID!
  text: String!
  tags: [String]
}
```

```graphql filename="comments.graphql"
type Comment {
  id: ID!
  text: String!
}
```

Running `loadSchema` produces the following output:

```graphql
type Query {
  posts: [Post]
}

type Post {
  comments: [Comment]
  id: ID!
  text: String!
  tags: [String]
}

type Comment {
  id: ID!
  text: String!
}
```

### Binding to HTTP Server

You can extend the loaded schema with resolvers

```ts
import { createServer } from 'http'
import { join } from 'node:path'
import { createYoga } from 'graphql-yoga'
import { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader'
import { loadSchema } from '@graphql-tools/load'
import { addResolversToSchema } from '@graphql-tools/schema'

async function main() {
  // Load schema from the file
  const schema = await loadSchema(join(__dirname, './schema.graphql'), {
    loaders: [new GraphQLFileLoader()]
  })

  // Write some resolvers
  const resolvers = {}

  // Add resolvers to the schema
  const schemaWithResolvers = addResolversToSchema({ schema, resolvers })

  const yoga = createYoga({
    schema: schemaWithResolvers
  })

  const server = createServer(yoga)

  server.listen(4000, () => {
    console.log('Server is running on http://localhost:4000')
  })
}

main().catch(error => {
  console.error(error)
  process.exit(1)
})
```

## Loaders

There are a lot of loaders that load your schemas and documents from different sources. You need to
provide those loaders under `loaders` parameter like below:

<Callout>
  Watch [Episode #22 of
  `graphql.wtf`](https://graphql.wtf/episodes/22-graphql-schema-file-loading-with-graphql-tools) for
  a quick introduction to file loaders:
</Callout>

<iframe src="https://www.youtube.com/embed/I79_b7K0rIk" />

### `GraphQLFileLoader`

This loader loads your GraphQLSchema from `.graphql` files like below:

```ts
import { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader'
import { loadSchema } from '@graphql-tools/load'
import { addResolversToSchema } from '@graphql-tools/schema'

// schema is `GraphQLSchema` instance
const schema = await loadSchema('schema.graphql', {
  // load from a single schema file
  loaders: [new GraphQLFileLoader()]
})

// You can add resolvers to that schema
const schemaWithResolvers = addResolversToSchema({
  schema,
  resolvers: {
    Query: {
      //  ...
    }
  }
})
```

This loader also supports glob pattern;

```ts
import { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader'
import { loadSchema } from '@graphql-tools/load'

const schema = await loadSchema('graphql/**/*.graphql', {
  // load files and merge them into a single schema object
  loaders: [new GraphQLFileLoader()]
})
```

If you use `loadDocuments`, it gives you an array of document source objects;

```ts
import { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader'
import { loadDocuments } from '@graphql-tools/load'

const documents = await loadDocuments('graphql/**/*.graphql', {
  // load files and merge them into a single schema object
  loaders: [new GraphQLFileLoader()]
})
```

<Callout>
  This loader only supports Node environment because it relies on File System of your platform.
</Callout>

### `JsonFileLoader`

This loader handles schema introspection and document nodes in `.json` files.

Introspection is handled in the example below:

```ts
import { JsonFileLoader } from '@graphql-tools/json-file-loader'
import { loadSchema } from '@graphql-tools/load'
import { addMocksToSchema } from '@graphql-tools/mock'

const schema = await loadSchema('schema-introspection.json', {
  loaders: [new JsonFileLoader()]
})

// Mocked non-executable schema generated from an introspection
const mockedSchema = addMocksToSchema({ schema })
```

This loader handles `json` files if they represent `DocumentNode`, and returns an array of document
sources.

```ts
import { JsonFileLoader } from '@graphql-tools/json-file-loader'
import { loadDocuments } from '@graphql-tools/load'

const documents = await loadDocuments('**/*-document.json', {
  loaders: [new JsonFileLoader()]
})
```

<Callout>
  This loader only supports Node environment because it relies on File System of your platform.
</Callout>

### `CodeFileLoader`

This loader extracts GraphQL SDL string, exported `GraphQLSchema` and `DocumentNode` from TypeScript
and JavaScript code files. Let's say you have the following code file;

```ts
const ME_QUERY = gql`
  query Me {
    me {
      id
      name
      username
      age
    }
  }
`
```

And the following code will extract `Me` query operation from that code file without executing it
using [GraphQL Tag Pluck](/docs/graphql-tag-pluck). It understands `/* GraphQL */` magic comment and
`gql` literals. You can configure [GraphQL Tag Pluck](/docs/graphql-tag-pluck) using `pluckConfig`.

```ts
import { CodeFileLoader } from '@graphql-tools/code-file-loader'
import { loadDocuments } from '@graphql-tools/load'

const documents = await loadDocuments('./src/**/graphql/*.ts', {
  loaders: [new CodeFileLoader()],
  pluckConfig: {
    // ...
  }
})
```

You can also load your schema from code files like below:

```ts
import { GraphQLSchema } from 'graphql'

// typeDefs.ts
export const typeDefs = /* GraphQL */ `
  type Query {
    foo: String
  }
`
// or schema.ts
export const schema = new GraphQLSchema(/* ... */)
```

<Callout type="warning">
  This loader only supports Node.js environment because it relies on File System of your platform.
</Callout>

<Callout type="warning">
  **Note**: If you are using typescript and [path
  aliases](https://typescriptlang.org/tsconfig#paths), you may also need
  [tsconfig-paths](https://npmjs.com/package/tsconfig-paths). Further reading can be found at the
  [GitHub issue.](https://github.com/ardatan/graphql-tools/issues/1544)
</Callout>

### `UrlLoader`

This loader generates
[(a fully executable remote schema using @graphql-tools/wrap)](/docs/remote-schemas) from a URL
endpoint.

```ts
import { loadSchema } from '@graphql-tools/load'
import { UrlLoader } from '@graphql-tools/url-loader'

const schema = await loadSchema('http://localhost:3000/graphql', {
  loaders: [new UrlLoader()]
})
```

You can provide custom headers, HTTP method and custom W3C fetch method.

```ts
import { loadSchema } from '@graphql-tools/load'
import { UrlLoader } from '@graphql-tools/url-loader'

const schema = await loadSchema('http://localhost:3000/graphql', {
  loaders: [new UrlLoader()],
  headers: {
    Accept: 'application/json'
  },
  method: 'POST',
  fetch: myFetch
})
```

<Callout emoji="🪄">This loader supports both browser and node environments.</Callout>

In browser this remote schema can be called using vanilla GraphQL-js and act like a simple GraphQL
client.

```ts
import { graphql } from 'graphql'
import { loadSchema } from '@graphql-tools/load'
import { UrlLoader } from '@graphql-tools/url-loader'

const schema = await loadSchema('http://localhost:3000/graphql', {
  loaders: [new UrlLoader()]
})

const response = await graphql(
  schema,
  /* GraphQL */ `
    {
      foo {
        bar {
          baz
        }
      }
    }
  `
)

console.log(response)
```
